#!/usr/bin/env python
# -*- coding: utf-8 -*-
from typing import Any
# pip install chardet
import chardet
import os
import sys
import re
import json
from pathlib import Path

import datetime
import subprocess

today = datetime.datetime.today()
COPYRIGHT_BIN  = 'Copyright © 1998-%d Glodon' % today.year
COPYRIGHT_CODE = 'Copyright © 1998-%d Glodon Company Limited.' % today.year
LEGALTRADEMARK = 'Glodon'
PROJECT_RELPATH= 'gdmplab\\'
PROJECT_NAME   = 'AppGdmpLab'
class CheckConfig(object):
    def __init__(self, jsonf: str):
        # Opening JSON file
        f = open(os.path.abspath(jsonf), 'r',encoding='UTF-8')
        self.config = json.load(f)
    def __getattr__(self, __name: str):
        return self.config[__name]

class CodeCheck(object):
    """
    代码检查:
    1. copyright
    2. DBG_WARN date & author
    3. EnableCompileWarning_The_LAST_IncludeInCpp.h
    4. OwnerPtr定义的变量是否以op开头
    5. 是否使用了tab作为缩进
    """
    def __init__(self, config):
        self.config = config
        selfPath = os.path.abspath(__file__)
        basePath = os.path.abspath(os.path.join(selfPath,'..\\..\\..\\source'))
        self.config.base = basePath
    def __matchSuffix(self,f):
        """判断是否是指定后缀"""
        for str in self.config.suffix:
            if f.endswith(str):
                return True
        return False

    def __findAllSource(self,base):
        path = Path(self.config.base)
        ignores = [ str(path.joinpath(i)) for i in self.config.ignores]
        for root, ds, fs in os.walk(base):
            ds[:] = [d for d in ds if os.path.join(root, d) not in ignores]
            if root.find("\\DotNet\\") >-1 and root.find("\\obj\\") >-1:
                ds[:]=[]
                fs[:]=[]
            for f in fs:
                if self.__matchSuffix(f):
                    path = os.path.join(root, f)
                    if path in ignores:
                        continue
                    yield path

    @staticmethod
    def get_encoding(filename):
        """以二进制方式读取文件，获取字节数据，检测编码格式"""
        with open(filename, 'rb') as f:
            return chardet.detect(f.read())['encoding']

    @staticmethod
    def file_read(filename, file_encoding):
        """以检测到的文件编码格式来读取文件"""
        with open(filename, 'r', encoding=file_encoding) as f:
            file_content = f.read()
            return file_content
    @staticmethod
    def findCopyrightFlag(content):
        pos = content.find(COPYRIGHT_CODE)
        if pos < 0:
            return False
        if content.find('////////',0,pos) < 0:
            return False
        return True

    def opCheck(self, file_content, errors):
        op_check= re.findall(r"OwnerPtr<.+>\s+(?!op)[\w_():]+", file_content, re.M)
        if len(op_check) > 0:
            for op in op_check:
                if op.find('m_op') > 0:#ignore m_ var
                    continue
                if op.find('s_op') > 0:#ignore s_ var
                    continue
                if re.search(r"OwnerPtr<[\w:_]+?>>", op):#ignore vector<OwnerPtr<xxx>>
                    continue
                if re.search(r"OwnerPtr<[\w:_]+?>\(", op):
                    continue
                if re.search(r"OwnerPtr<[\w:_]+?>\s+[\w_]+", op) == None:
                    continue
                if re.search(r"OwnerPtr<[\w:_]+?>\s+[\w_:]+\(", op):#ignore func()
                    continue
                else:
                    errors.append(op)
        return errors

    def is_subdirectory(self, file_path, folder_paths):
        file_path = Path(file_path).resolve()
        for folder_path in folder_paths:
            folder_path = Path(folder_path).resolve()
            try:
                file_path.relative_to(folder_path)
                return True
            except ValueError:
                continue
        return False

    def check(self):
        path = Path(self.config.base)
        # fp = path.joinpath("Samples")
        fp = path
        ignores_ownerPtr = [ str(path.joinpath(i)) for i in self.config.ignores_ownerPtr]

        dt = self.config.dbgDate
        for filename in self.__findAllSource(fp):
            file_encoding = self.get_encoding(filename)
            file_content = self.file_read(filename,  file_encoding)
            errors = []
            if not self.findCopyrightFlag(file_content):
                errors.append('no copyright found')
                pass
            if file_content.find('\t') > -1:
                errors.append('should replace tab as 4 blanks')
                pass
            warn_check = re.search(r"EnableCompileWarning_The_LAST_IncludeInCpp.h\".+#include", file_content, re.S)
            if warn_check:
                errors.append('EnableCompileWarning_The_LAST_IncludeInCpp位置错误')
                pass
            dbg_check = re.findall(r'DBG_.*L"(?!GDMPLab)[^"]+",\s?L"(?!'+dt+')[^"]+"\)', file_content)
            if len(dbg_check) > 0:
                for op in dbg_check:
                    errors.append(op)


            if(self.is_subdirectory(filename, ignores_ownerPtr)):
                if len(errors) > 0:
                    print(filename)
                for s in errors:
                    print(s)
                continue
            else:
                errors = self.opCheck(file_content, errors)
                if len(errors) > 0:
                    print(filename)
                    for s in errors:
                        print(s)
class NameSpaceAndTryCatchCheck(CodeCheck):
    """
    C#规范检查：
    1、命名空间是否以Glodon.Gdmp.SDK.Samples.开头
    2、_camelCasing、camelCasing、PascalCasing这三种命名规范对应的代码命名是否有违例
    3、是否在实现ICommand的类中的Execute方法中用try catch包裹全部代码
    """
    def check(self):
        path = Path(self.config.base)
        # dotnetSampleSln = path.joinpath(path, 'Samples\\DotNet\\Source\\SDKSamples.sln')
        # 运行C#代码分析工具
        # subprocess.run('Glodon.GdmpAnalyzer.Client\\gdmpAnalyzer.Client.exe {}'.format(dotnetSampleSln))
class PackageCheck(object):
    def __init__(self, cfg: str) -> None:
        self.cfg = CheckConfig(cfg)

    def Check(self):
        # 代码检查
        cc = CodeCheck(self.cfg)
        cc.check()

    def CheckCSharp(self):
        cc = NameSpaceAndTryCatchCheck(self.cfg)
        cc.check()

if __name__ == "__main__":
    cfg = "config_dev.json"
    if len(sys.argv) > 1:
        cfg = sys.argv[1]

    ck = PackageCheck(cfg)
    ck.Check()
    ck.CheckCSharp()
